package util

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

// ParseFormToStruct 用map填充结构
func ParseFormToStruct(data map[string][]string, obj interface{}) {
	//dataToStruct(data, obj)
	structGetData(data, obj)
}

func getFieldReflectValue(obj interface{}, name string) reflect.Value {
	structValue := reflect.ValueOf(obj).Elem() //结构体属性值
	newName := fmt.Sprintf("%s%s", strings.ToUpper(string(name[0])), name[1:])
	newName = strings.Replace(newName, "[", "", -1)
	newName = strings.Replace(newName, "]", "", -1)
	return structValue.FieldByName(newName) //结构体单个属性值
}

func dataToStruct(data map[string][]string, obj interface{}) {
	//从data反射obj太耗性能
	for k, v := range data {
		structFieldValue := getFieldReflectValue(obj, k)
		if !structFieldValue.IsValid() {
			continue
		}
		if !structFieldValue.CanSet() {
			continue
		}
		err := setStructField(structFieldValue, v)
		if err != nil {
			continue
		}
	}
}

func structGetData(data map[string][]string, obj interface{}) {
	// 反射结构体的数据
	structValue := reflect.TypeOf(obj)
	if structValue.Kind() == reflect.Ptr {
		structValue = structValue.Elem()
	}

	if structValue.Kind() != reflect.Struct {
		return
	}

	fieldNum := structValue.NumField()
	for i := 0; i < fieldNum; i++ {
		structField := structValue.Field(i)
		fieldName := structField.Name
		structFieldValue := getFieldReflectValue(obj, fieldName)
		if !structFieldValue.IsValid() {
			continue
		}

		// 用名字可以找到
		if val, ok := data[fieldName]; ok {
			setStructField(structFieldValue, val)
			continue
		}

		// toLower
		newLowerName := fmt.Sprintf("%s%s", strings.ToLower(string(fieldName[0])), fieldName[1:])
		if val, ok := data[newLowerName]; ok {
			setStructField(structFieldValue, val)
			continue
		}

		// 尝试用tag找
		tags := strings.Split(string(structField.Tag), "\"")
		tagName := ""
		if len(tags) > 1 {
			tagName = tags[1]
			if val, ok := data[tagName]; ok {
				setStructField(structFieldValue, val)
				continue
			}
		}

		// 数组
		if structFieldValue.Kind() == reflect.Slice {
			sliceName := fieldName + "[]"
			if val, ok := data[sliceName]; ok {
				setStructField(structFieldValue, val)
				continue
			}

			sliceLowerName := newLowerName + "[]"
			if val, ok := data[sliceLowerName]; ok {
				setStructField(structFieldValue, val)
				continue
			}

			sliceTagName := tagName + "[]"
			if val, ok := data[sliceTagName]; ok {
				setStructField(structFieldValue, val)
				continue
			}
		}
	}
}

// 用map的值替换结构的值
func setStructField(structFieldValue reflect.Value, values []string) error {
	switch structFieldValue.Kind() {
	case reflect.Slice:
		slice := reflect.MakeSlice(structFieldValue.Type(), 0, len(values))
		for _, value := range values {
			val, err := getStructFieldValue(structFieldValue.Type().Elem(), value)
			if err != nil {
				return err
			}
			slice = reflect.Append(slice, val)
		}
		structFieldValue.Set(slice)
	case reflect.Ptr:
		if len(values) <= 0 {
			return nil
		}

		val, err := getStructFieldValue(structFieldValue.Type().Elem(), values[0])
		if err != nil {
			return err
		}

		p := reflect.New(structFieldValue.Type().Elem())
		p.Elem().Set(val)
		structFieldValue.Set(p)
	default:
		if len(values) <= 0 {
			return nil
		}

		val, err := getStructFieldValue(structFieldValue.Type(), values[0])
		if err != nil {
			return err
		}
		structFieldValue.Set(val)
	}

	return nil
}

func getStructFieldValue(groove reflect.Type, value string) (reflect.Value, error) {
	switch groove.Kind() {
	case reflect.String:
		return reflect.ValueOf(value), nil
	case reflect.Int:
		i, err := strconv.Atoi(value)
		if err != nil {
			return reflect.ValueOf(0), err
		}
		return reflect.ValueOf(i), nil
	case reflect.Bool:
		b := !(value == "" || strings.ToLower(value) == "0" || strings.ToLower(value) == "false")
		return reflect.ValueOf(b), nil
	case reflect.Int8:
		i, err := strconv.ParseInt(value, 10, 64)
		if err != nil {
			return reflect.ValueOf(0), err
		}
		return reflect.ValueOf(int8(i)), nil
	case reflect.Int32:
		i, err := strconv.ParseInt(value, 10, 32)
		if err != nil {
			return reflect.ValueOf(0), err
		}
		return reflect.ValueOf(int64(i)), nil
	case reflect.Uint32:
		i, err := strconv.ParseUint(value, 10, 32)
		if err != nil {
			return reflect.ValueOf(uint32(0)), err
		}
		return reflect.ValueOf(uint32(i)), nil
	case reflect.Int64:
		i, err := strconv.ParseInt(value, 10, 64)
		if err != nil {
			return reflect.ValueOf(0), err
		}
		return reflect.ValueOf(i), nil
	case reflect.Uint64:
		i, err := strconv.ParseUint(value, 10, 64)
		if err != nil {
			return reflect.ValueOf(0), err
		}
		return reflect.ValueOf(i), nil
	case reflect.Float32:
		i, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return reflect.ValueOf(0), err
		}
		return reflect.ValueOf(float32(i)), nil
	case reflect.Float64:
		i, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return reflect.ValueOf(0), err
		}
		return reflect.ValueOf(i), nil
	}
	return reflect.ValueOf(0), errors.New("type error")
}
